package com.linkhub.oms.admin.modules.stock.service.impl;

import cn.hutool.core.bean.copier.CopyOptions;
import com.google.common.collect.Lists;
import com.linkhub.oms.admin.common.exception.ExceptionFactory;
import com.linkhub.oms.admin.modules.stock.domain.dto.ConfirmCombineProcessingDTO;
import com.linkhub.oms.admin.modules.stock.domain.dto.ConfirmInboundDTO;
import com.linkhub.oms.admin.modules.stock.domain.dto.ConfirmOutboundDTO;
import com.linkhub.oms.admin.modules.stock.domain.dto.StockTransferDTO;
import com.linkhub.oms.admin.modules.stock.enums.BusinessType;
import com.linkhub.oms.admin.modules.stock.enums.ProductType;
import com.linkhub.oms.common.enums.IEnumConvert;
import com.linkhub.oms.common.util.CacheKeyAssist;
import com.linkhub.oms.common.util.QtBeanUtil;
import com.linkhub.oms.common.util.WmsUtil;
import com.linkhub.oms.common.util.json.QtFastjsonUtil;
import com.linkhub.oms.component.redis.locker.DistributedLocker;
import com.linkhub.oms.dao.mapper.*;
import com.linkhub.oms.dao.mpservice.IPhysicalStockFlowService;
import com.linkhub.oms.dao.mpservice.IProductStockService;
import com.linkhub.oms.entity.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;

/**
 * @ClassName PhysicalStockFlowServiceImpl
 * @Description TODO
 * @Author XieYu
 * @Date 2024/6/25 22:00
 * @Version 1.0
 */
@Slf4j
@Service
public class PhysicalStockServiceImpl {
    @Resource
    private IPhysicalStockFlowService physicalStockService;
    @Resource
    private DistributedLocker distributedLocker;
    @Resource
    private IProductStockService productStockService;

    @Transactional(rollbackFor = Exception.class)
    public void wmsInboundConfirm(BusinessType businessType, Long batchId, ConfirmInboundDTO confirmInboundDTO) {
        log.info("库存管理-入库单确认-实物流水-开始，batchId:{}, {}", batchId, QtFastjsonUtil.toJSONString(confirmInboundDTO));
        // 处理当前批次相关的流水
        PhysicalStockFlowEntity flow = new PhysicalStockFlowEntity();
        flow.setAmount(confirmInboundDTO.getSkuQuantity());
        flow.setBusinessType(businessType.getCode());
        flow.setUserId(confirmInboundDTO.getUserId());
        flow.setSkuId(confirmInboundDTO.getSkuId());
        flow.setStashName(confirmInboundDTO.getStashName());
        flow.setBusinessNo(confirmInboundDTO.getInboundNo());
        ProductType productType = IEnumConvert.valueOf(confirmInboundDTO.getType(), ProductType.class);
        flow.setType(productType.getValue());
        flow.setBatchId(batchId);

        String lockKey = CacheKeyAssist.getPhysicalStockUpdatedLockKey(confirmInboundDTO.getUserId(),
                confirmInboundDTO.getSkuId(), confirmInboundDTO.getStashName());
        if (!distributedLocker.tryLock(lockKey)) {
            log.error("入库单确认更新实物库存时获取锁失败");
            throw ExceptionFactory.getI18nBizException("stock.physical.updated.concurrence.error");
        }
        try {
            ProductStockEntity productStock = productStockService.get(confirmInboundDTO.getUserId(),
                    confirmInboundDTO.getStashName(), confirmInboundDTO.getSkuId());
            if (Objects.isNull(productStock)) { // 组合加工场景下，会为null
                productStock = ProductStockEntity.createDefaultStock(confirmInboundDTO.getUserId(),
                        confirmInboundDTO.getSkuId(), confirmInboundDTO.getStashName());
            }
            if (ProductType.GOOD == productType) {
                int goodStock = Objects.isNull(productStock) ? 0 : productStock.getGoodStock();
                flow.setBeforeAmount(goodStock);
                productStock.setGoodStock(goodStock + flow.getAmount());
            } else {
                int badStock = Objects.isNull(productStock) ? 0 : productStock.getBadStock();
                flow.setBeforeAmount(badStock);
                productStock.setBadStock(badStock + flow.getAmount());
            }
            flow.setAfterAmount(flow.getBeforeAmount() + flow.getAmount());
            physicalStockService.save(flow);
            productStockService.saveOrUpdate(productStock);
        } finally {
            distributedLocker.unlock(lockKey);
        }
        log.info("库存管理-入库单确认-实物流水-结束");
    }

    @Transactional(rollbackFor = Exception.class)
    public void wmsOutboundConfirm(ConfirmOutboundDTO confirmOutboundDTO,
                                   List<PreemptionStockFlowEntity> preemptionStockFlowEntityList) {
        log.info("库存管理-出库单确认-实物流水-开始，参数：{}，预占流水：{}", confirmOutboundDTO, preemptionStockFlowEntityList);

        String lockKey = CacheKeyAssist.getPhysicalStockUpdatedLockKey(confirmOutboundDTO.getUserId(),
                confirmOutboundDTO.getSkuId(), confirmOutboundDTO.getStashName());
        if (!distributedLocker.tryLock(lockKey)) {
            log.error("出库单确认更新实物库存时获取锁失败");
            throw ExceptionFactory.getI18nBizException("stock.physical.updated.concurrence.error");
        }
        try {
            ProductStockEntity productStock = productStockService.get(confirmOutboundDTO.getUserId(),
                    confirmOutboundDTO.getStashName(), confirmOutboundDTO.getSkuId());
            List<PhysicalStockFlowEntity> physicalStockFlowEntities = Lists.newArrayListWithCapacity(preemptionStockFlowEntityList.size());
            for (PreemptionStockFlowEntity preemptionStockFlowEntity : preemptionStockFlowEntityList) {
                PhysicalStockFlowEntity physicalStockFlowEntity = new PhysicalStockFlowEntity();
                QtBeanUtil.copyProperties(preemptionStockFlowEntity, physicalStockFlowEntity,
                        CopyOptions.create().setIgnoreProperties("id", "createTime", "updateTime"));
                physicalStockFlowEntity.setBusinessType(IEnumConvert.valueOf(preemptionStockFlowEntity.getBusinessType(), BusinessType.class).getCode());
                physicalStockFlowEntity.setAmount(-preemptionStockFlowEntity.getAmount());
                physicalStockFlowEntity.setBeforeAmount(productStock.getGoodStock());
                physicalStockFlowEntity.setAfterAmount(productStock.getGoodStock() - preemptionStockFlowEntity.getAmount());
                physicalStockFlowEntity.setType(ProductType.GOOD.getValue());
                physicalStockFlowEntities.add(physicalStockFlowEntity);
                productStock.setGoodStock(productStock.getGoodStock() - preemptionStockFlowEntity.getAmount());
            }
            physicalStockService.saveBatch(physicalStockFlowEntities);
            productStockService.updateById(productStock);
        } finally {
            distributedLocker.unlock(lockKey);
        }

        log.info("库存管理-出库单确认-实物流水-结束");
    }

    @Transactional(rollbackFor = Exception.class)
    public void wmsOutboundConfirm(ConfirmOutboundDTO confirmOutboundDTO) {
        log.info("库存管理-出库单确认-实物耗材流水-开始，参数：{}", confirmOutboundDTO);

        String lockKey = CacheKeyAssist.getPhysicalStockUpdatedLockKey(confirmOutboundDTO.getUserId(),
                confirmOutboundDTO.getSkuId(), confirmOutboundDTO.getStashName());
        if (!distributedLocker.tryLock(lockKey)) {
            log.error("出库单确认更新实物耗材库存时获取锁失败");
            throw ExceptionFactory.getI18nBizException("stock.physical.updated.concurrence.error");
        }
        try {
            ProductStockEntity productStock = productStockService.get(confirmOutboundDTO.getUserId(),
                    confirmOutboundDTO.getStashName(), confirmOutboundDTO.getSkuId());
            if (productStock.getGoodStock() < confirmOutboundDTO.getSkuQuantity()) {
                log.error("库存管理-出库单确认-实物耗材不够，剩余：{}，此次消耗：{}", productStock.getGoodStock(), confirmOutboundDTO.getSkuQuantity());
                throw ExceptionFactory.getI18nBizException("stock.physical.updated.concurrence.error");
            }
            PhysicalStockFlowEntity physicalStockFlowEntity = new PhysicalStockFlowEntity();
            physicalStockFlowEntity.setBusinessType(BusinessType.CONSUMABLE_OUT.getCode());
            physicalStockFlowEntity.setUserId(confirmOutboundDTO.getUserId());
            physicalStockFlowEntity.setSkuId(confirmOutboundDTO.getSkuId());
            physicalStockFlowEntity.setStashName(confirmOutboundDTO.getStashName());
            physicalStockFlowEntity.setBusinessNo(confirmOutboundDTO.getBusinessNo());
            physicalStockFlowEntity.setBatchId(0L);
            physicalStockFlowEntity.setAmount(-confirmOutboundDTO.getSkuQuantity());
            physicalStockFlowEntity.setBeforeAmount(productStock.getGoodStock());
            physicalStockFlowEntity.setAfterAmount(productStock.getGoodStock() - confirmOutboundDTO.getSkuQuantity());
            physicalStockFlowEntity.setType(ProductType.GOOD.getValue());
            productStock.setGoodStock(productStock.getGoodStock() - confirmOutboundDTO.getSkuQuantity());

            physicalStockService.save(physicalStockFlowEntity);
            productStockService.updateById(productStock);
        } finally {
            distributedLocker.unlock(lockKey);
        }

        log.info("库存管理-出库单确认-实物耗材流水-结束");
    }

    @Transactional(rollbackFor = Exception.class)
    public void wmsCombineProcessingConfirm(ConfirmCombineProcessingDTO confirmCombineProcessingDTO,
                                            List<PreemptionStockFlowEntity> preemptionStockFlowEntityList) {

        log.info("库存管理-组合加工确认-实物流水-开始，参数：{}，预占流水：{}", confirmCombineProcessingDTO, preemptionStockFlowEntityList);

        List<PhysicalStockFlowEntity> physicalStockFlowEntities = Lists.newArrayListWithCapacity(preemptionStockFlowEntityList.size());
        for (PreemptionStockFlowEntity preemptionStockFlowEntity : preemptionStockFlowEntityList) {
            String lockKey = CacheKeyAssist.getPhysicalStockUpdatedLockKey(confirmCombineProcessingDTO.getUserId(),
                    preemptionStockFlowEntity.getSkuId(), confirmCombineProcessingDTO.getStashName());
            if (!distributedLocker.tryLock(lockKey)) {
                log.error("出库单确认更新实物库存时获取锁失败");
                throw ExceptionFactory.getI18nBizException("stock.physical.updated.concurrence.error");
            }

            try {
                ProductStockEntity productStock = productStockService.get(confirmCombineProcessingDTO.getUserId(),
                        confirmCombineProcessingDTO.getStashName(), preemptionStockFlowEntity.getSkuId());

                PhysicalStockFlowEntity physicalStockFlowEntity = new PhysicalStockFlowEntity();
                QtBeanUtil.copyProperties(preemptionStockFlowEntity, physicalStockFlowEntity,
                        CopyOptions.create().setIgnoreProperties("id", "createTime", "updateTime"));
                physicalStockFlowEntity.setAmount(-preemptionStockFlowEntity.getAmount());
                physicalStockFlowEntity.setType(ProductType.GOOD.getValue());
                physicalStockFlowEntity.setBeforeAmount(productStock.getGoodStock());
                physicalStockFlowEntity.setAfterAmount(productStock.getGoodStock() - preemptionStockFlowEntity.getAmount());
                physicalStockFlowEntities.add(physicalStockFlowEntity);
                productStock.setGoodStock(productStock.getGoodStock() - preemptionStockFlowEntity.getAmount());
                productStockService.updateById(productStock);
            } finally {
                distributedLocker.unlock(lockKey);
            }
        }
        physicalStockService.saveBatch(physicalStockFlowEntities);

        log.info("库存管理-组合加工确认-实物流水-结束");
    }

}
